package value;

import globals.CentralStatics;
import globals.Tuple;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Iterator;

import problems.Actions;

public class AlphaMatrix<A> extends SimpleVector<A> implements Vector, Taggable<A> {

	private double[][] matValue;
	//Note that an [int index] is inherited from SimpleVector 
	
	private int dX, dY;
	public A theTag;
	
	public AlphaMatrix(int dimX, int dimY){
		super();
		this.matValue = new double[dimX][dimY];
	    this.dX=dimX; this.dY=dimY;
	    for(int i=0; i<dX; i++){
	    	for(int j=0; j<dY; j++){
	    		matValue[i][j] = 0;
	    	}
	    }
	    theTag = null;
	}
	
	public AlphaMatrix(AlphaMatrix<A> a, AlphaMatrix<A> b, boolean copyTagRight){
		this.dX=a.getNumberOfStates(); this.dY=a.getNumberOfObjectives();
		this.matValue = new double[dX][dY];
	    for(int i=0; i<dX; i++){
	    	for(int j=0; j<dY; j++){
	    		matValue[i][j] = a.getValue(i,j) + b.getValue(i,j);
	    	}
	    }
	    if(copyTagRight){
	    	this.theTag = b.getAction();
	    } else {
	    	this.theTag = null;
	    }
	}
	
	public AlphaMatrix(AlphaMatrix<?> a, A action){
		this.dX=a.getNumberOfStates(); this.dY=a.getNumberOfObjectives();
		this.matValue = new double[dX][dY];
	    for(int i=0; i<dX; i++){
	    	for(int j=0; j<dY; j++){
	    		matValue[i][j] = a.getValue(i,j);
	    	}
	    }
	    this.theTag = action;
	}
	
	public AlphaMatrix(AlphaMatrix<?> a){
		this.dX=a.getNumberOfStates(); this.dY=a.getNumberOfObjectives();
		this.matValue = new double[dX][dY];
	    for(int i=0; i<dX; i++){
	    	for(int j=0; j<dY; j++){
	    		matValue[i][j] = a.getValue(i,j);
	    	}
	    }
	    this.theTag = null;
	}
	
	@Override
	public Iterator<Double> iterator () {
		index = 0;
		return this;
	}

	@Override
	public boolean hasNext() {
		return index < dX*dY;
	}

	@Override
	public Double next() {
		Tuple<Integer,Integer> tup = this.toXY(index);
		index++;
		return this.matValue[tup.x][tup.y];
	}

	@Override
	public void remove() {
		System.err.println("Do not use AlphaMatrix.remove()");
	}

	@Override
	public double linearScalValue(double[] wvec) {
		return CentralStatics.innerProduct(this.getValue(), wvec);
	}

	@Override
	public boolean equalValues(Vector v) {
		return CentralStatics.equalVectors(this.getValue(), v.getValue());
	}

	@Override
	public boolean weakParetoDominates(Vector v) {
		return CentralStatics.weakParetoDominates(this.getValue(), v.getValue());
	}

	@Override
	public boolean paretoDominates(Vector v) {
		if(this.equals(v)){
			return false;
		} else {
			return this.weakParetoDominates(v);
		}
	}

	@Override
	public void setValue(double[] vec) {
		int l = this.length();
		for(int i=0;i<l;i++){
			this.setValue(i, vec[i]);
		}
	}

	@Override
	public double[] getValue() {
		int l = this.length();
		double[] dbl = new double[l];
		for(int i=0;i<l;i++){
			dbl[i] = this.getValue(i);
		}
		return dbl;
	}

	@Override
	public int length() {
		return dX*dY;
	}

	@Override
	public void setValue(int i, double val) {
		Tuple<Integer,Integer> tup = this.toXY(i);
		matValue[tup.x][tup.y] = val;
	}

	@Override
	public double getValue(int i) {
		Tuple<Integer,Integer> tup = this.toXY(i);
		return matValue[tup.x][tup.y];
	}
	
	public double getValue(int s, int obj) {
		return matValue[s][obj];
	}
	
	public void setValue(int s, int obj, double val) {
		matValue[s][obj] = val;
	}
	
	private Tuple<Integer,Integer> toXY(int n){
		Tuple<Integer,Integer> result = new Tuple<Integer,Integer>(0,0);
		result.x = n/this.dY;
		result.y = n%this.dY;
		return result;
	}

	@Override
	public void tag(A t) {
		this.theTag = t;
	}

	@Override
	public void tag(ArrayList<A> tl) {
		System.err.println("AlphaMatrix should only be tagged with one action in A.");
		System.err.println("Tagging with the first action in the list.");
		this.theTag = tl.get(0);
	}

	@Override
	public ArrayList<A> retrieveTags() {
		ArrayList<A> lst = new ArrayList<A>(1);
		lst.add(theTag);
		return lst;
	}
	
	public A getAction() {
		return theTag;
	}
	
	public int getNumberOfStates(){
		return this.dX;
	}
	
	public int getNumberOfObjectives(){
		return this.dY;
	}
	/*private int fromXY(Tuple<Integer,Integer> tup){
		int result;
		result = tup.x.intValue()*this.dY;
		result += tup.y;
		return result;
	}*/
	
	//NB, the following methods return a NEW AlphaMatrix!
	@Override
	public AlphaMatrix<A> add(SimpleVector<?> v2){
		AlphaMatrix<A> sv = new AlphaMatrix<A>(this.dX, this.dY);
		sv.setValue(CentralStatics.addVec(this.getValue(), v2.getValue()));
		return sv;
	}
	
	public AlphaMatrix<A> add(AlphaMatrix<A> v2){
		AlphaMatrix<A> sv = new AlphaMatrix<A>(this, v2, false);
		return sv;
	}

	@Override
	public void process(){
		//System.err.println("Nothing to process, called AlphaMatrix method.");
	}
	
	public AlphaMatrix<A> copyMatrix(AlphaMatrix<A> v) {
		return new AlphaMatrix<A>(v);
	}
	
	public AlphaMatrix<A> copyMatrix(SimpleVector<A> v) {
		AlphaMatrix<A> am = new AlphaMatrix<A>(this);
		am.setValue(v.getValue());
		A tg = null;
		am.tag(tg);
		return am;
	}
	
	@Override
	public AlphaMatrix<A>  zeroVector() {
		AlphaMatrix<A> sv = new AlphaMatrix<A>(this.dX,this.dY);
		return sv;
	}
	
	@Override
	public AlphaMatrix<A> linearTransformation(double[][] trans) {
		if(trans.length!=this.getNumberOfStates()){
			System.err.println("inapplicable transformation, wrong number of states");
			System.exit(-238);
		}
		AlphaMatrix<A> sv = this.zeroVector();
		for(int s=0; s<this.getNumberOfStates(); s++){
			for(int ob=0; ob<this.getNumberOfObjectives(); ob++ ){
				double val = 0;
				for(int s_=0; s_<this.getNumberOfStates(); s_++){
					val += trans[s][s_]*this.getValue(s_, ob);
				}
				sv.setValue(s, ob, val);
			}
		}
		return sv;
	}
	
	@Override
	public void multiplyByScalar(double scalar) {
		for(int i=0; i<this.dX; i++){
			for(int j=0; j<this.dY; j++){
				this.matValue[i][j] = this.matValue[i][j]*scalar;
			}
		}
	}
	
	public String toString(String[] states, Actions<A> acts){
		DecimalFormat format = new DecimalFormat("###.##");
		String result = "Action: ";
		if(this.theTag!=null){
			result+=acts.printAction(this.theTag);
		}
		result+="\n";
		for(int s=0; s<this.getNumberOfStates(); s++){
			result+=states[s]+": ";
			for(int o=0; o<this.getNumberOfObjectives(); o++){
				result+=format.format(this.getValue(s, o))+" ";
			}
			result+="\n";
		}
		return result;
	}
	
	public double[] scalarizeWithWeights(double wvec[]){
		double[] result = new double[this.getNumberOfStates()];
		for(int i=0; i<result.length; i++){
			result[i]=0;
			for(int j=0; j<this.getNumberOfObjectives(); j++){
				result[i]+=this.matValue[i][j]*wvec[j];
			}
		}
		return result;
	}
	
	public double[] scalarizeWithBelief(double bvec[]){
		double[] result = new double[this.getNumberOfObjectives()];
		for(int i=0; i<result.length; i++){
			result[i]=0;
			for(int j=0; j<this.getNumberOfStates(); j++){
				result[i]+=this.matValue[j][i]*bvec[j];
			}
		}
		return result;
	}
	
	@Override
	public String className() {
		return "AlphaMatrix";
	}
}
